import type { SearchPageTitleCleanupConfig, SearchSelectorConfig } from '../index'
import type { PageSearchMetaSourceConfig, PageSearchMetaTimeConfig } from './index'
import { StyleModule } from '../../../../../../lib/abstract'
import { PageHandler } from '../../../../../../lib/page_handler'
import {
  GOOGLE_SEARCH_SELECTORS,
  GOOGLE_VIDEO_DYNAMIC_RESULT_CONTAINER_SELECTOR,
  GOOGLE_VIDEO_PAGE_SEARCH_RESULT_META_SELECTOR,
  GOOGLE_VIDEO_RESULT_LOADING_SELECTOR,
  GooglePageType,
} from '../../../../../../lib/page_handler/platform/google/types'
import { PollingHandler } from '../../../../../../lib/polling_handler'
import { ToolKit } from '../../../../../../lib/utils/toolkit'
import {
  AppStyleCollections,
  HOSTNAME,
  logger,
  SUPPORTED_PLATFORMS,
} from '../../../../../../types/constants'
import { SearchStyle } from './index'

export class GoogleStyle extends StyleModule {
  readonly name: string = 'GoogleStyle'
  private static instance: GoogleStyle
  private pageHandler: PageHandler = PageHandler.getInstance()
  private searchStyle: SearchStyle = SearchStyle.getInstance()
  private pollingHandler: PollingHandler = PollingHandler.getInstance()
  private indexObserver: MutationObserver | null = null
  private loadingObserver: MutationObserver | null = null
  private updateCount: number = 0
  private lastDisplayState: string | null = null
  private childObservers: Map<HTMLElement, MutationObserver> = new Map()
  private readonly pageType = this.pageHandler.pages.google.pageType
  private readonly selectors: SearchSelectorConfig = this.pageHandler.pages.google.getResultSelectors()

  constructor() {
    super()

    if (HOSTNAME !== SUPPORTED_PLATFORMS.GOOGLE)
      return

    this.init()
  }

  static getInstance(): GoogleStyle {
    if (!GoogleStyle.instance) {
      GoogleStyle.instance = new GoogleStyle()
    }
    return GoogleStyle.instance
  }

  /**
   * 初始化
   */
  init(): void {
    this.initIndexObserver()
    this.initVideoObserver()
    this.handleResult()
  }

  /**
   * 处理结果更新
   */
  private handleResult() {
    this.updateSearchStyle()
    this.initResultMeta()
    this.updateTitle()
    this.hideElements()
  }

  /**
   * 【主页】初始化观察器
   */
  private initIndexObserver() {
    if (![GooglePageType.Index, GooglePageType.IndexHk].includes(this.pageType as GooglePageType))
      return

    // 如果已经存在观察器，先断开
    this.stopObservation()

    // 预先定义节流函数，避免节流函数被重复创建导致节流失效
    const throttledUpdate = ToolKit.func.throttle(() => {
      logger.debug(`[${this.name}] 搜索建议已更新`)
      this.handleIndexSuggestionUpdate()
    }, 100)

    // 遮罩观察器
    this.indexObserver = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
          throttledUpdate()
        }
      })
    })

    const selector = GOOGLE_SEARCH_SELECTORS.index.suggestionContainer
    const target = document.querySelector(selector)

    if (!target) {
      logger.warn(`[${this.name}] [主页] 搜索建议容器未找到`, selector)
      return
    }

    // 监听变化
    this.indexObserver.observe(target, {
      childList: true,
      subtree: true,
    })

    logger.attention(`[${this.name}] 已启用搜索建议观察`)
  }

  /**
   * 处理【主页】搜索建议更新
   */
  private handleIndexSuggestionUpdate(): void {
    const keyword = ($(GOOGLE_SEARCH_SELECTORS.index.input).val() || '') as string

    $(GOOGLE_SEARCH_SELECTORS.index.suggestionItem).not(`[${AppStyleCollections.Search.attr.PROCESSED_HIGHLIGHT}="true"]`).each((_, el) => {
      const $element = $(el)

      this.searchStyle.highlightElement($element, [keyword])

      $element.attr(AppStyleCollections.Search.attr.PROCESSED_HIGHLIGHT, 'true')
    })
  }

  /**
   * 【视频】初始化观察器
   */
  private initVideoObserver(): void {
    if (this.pageType !== GooglePageType.Video)
      return

    // 如果已经存在观察器，先断开
    this.stopObservation()

    // 启用加载观察
    this.startLoadingObserver()

    // 启用结果观察
    const selector = GOOGLE_VIDEO_DYNAMIC_RESULT_CONTAINER_SELECTOR

    const handler = () => {
      this.startResultObserver()
      this.handleResult()
    }
    this.pollingHandler.api.awaitElement(selector, handler)

    logger.attention(`[${this.name}] 已启用搜索结果观察`)
  }

  /**
   * 观察搜索加载元素的显示状态变化
   */
  private startLoadingObserver() {
    // 创建观察
    this.loadingObserver = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
          const element = mutation.target as HTMLElement
          this.checkDisplayChange(element)
        }
      })
    })

    // 只观察目标区域而非整个 body
    const targetElement = document.querySelector(GOOGLE_VIDEO_RESULT_LOADING_SELECTOR)
    if (targetElement) {
      // 记录初始状态
      this.lastDisplayState = (targetElement as HTMLElement).style.display

      // 开始观察 style 属性变化
      this.loadingObserver.observe(targetElement, {
        childList: false,
        subtree: false,
        attributes: true,
        attributeFilter: ['style'],
      })
    }
    else {
      logger.warn(`[${this.name}] 未找到搜索加载元素： ${GOOGLE_VIDEO_RESULT_LOADING_SELECTOR}`)
    }
  }

  /**
   * 检查 display 属性变化
   */
  private checkDisplayChange(element: HTMLElement) {
    const currentDisplay = element.style.display
    // 【重新加载】空值变为 none
    const reloaded = !this.lastDisplayState && currentDisplay === 'none'

    if (reloaded) {
      this.handleUpdate(false)
    }

    // 更新最后记录的状态
    this.lastDisplayState = currentDisplay
  }

  /**
   * 观察动态结果容器的子元素变化
   */
  private startResultObserver() {
    // 初始观察已有子元素
    const targetContainerChildren = document.querySelector(GOOGLE_VIDEO_DYNAMIC_RESULT_CONTAINER_SELECTOR)?.children || []

    if (targetContainerChildren.length === 0) {
      logger.warn(`[${this.name}] 目标容器没有子元素，无法启用观察`)
      return
    }

    Array.from(targetContainerChildren).forEach((child) => {
      this.observeChildElement(child as HTMLElement)
    })
  }

  /**
   * 观察单个子元素的属性变化
   */
  private observeChildElement(element: HTMLElement) {
    // 检查是否已经观察过
    if (element.dataset.observed === 'true')
      return

    const observer = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        if (mutation.type === 'attributes' && mutation.attributeName === 'async-ei') {
          this.handleUpdate(true)
        }
      })
    })

    observer.observe(element, {
      attributes: true,
      attributeFilter: ['async-ei'],
      childList: false,
      subtree: false,
    })

    // 标记并保存观察器
    element.dataset.observed = 'true'
    this.childObservers.set(element, observer)
  }

  /**
   * 处理更新
   */
  handleContainerUpdate(): void {
    this.updateCount++
    logger.info(`[${this.name}] 处理第 ${this.updateCount} 次加载完成的结果`)
    this.markElement.markGoogleElements()
    this.handleResult()
  }

  /**
   * 使用双帧同步处理容器更新
   */
  private handleUpdate(enableDoubleRAF: boolean = true): void {
    if (!enableDoubleRAF) {
      this.handleContainerUpdate()
      return
    }

    // 处理结果更新
    ToolKit.advanced.performance.doubleRAF(() => this.handleContainerUpdate())
  }

  /**
   * 停止观察
   */
  stopObservation(): void {
    this.loadingObserver?.disconnect()
    this.loadingObserver = null
    this.lastDisplayState = null

    this.childObservers.forEach(observer => observer.disconnect())
    this.childObservers.clear()
  }

  /**
   * 初始化搜索样式
   */
  updateSearchStyle(forceUpdate?: boolean): void {
    const supportPages = [
      GooglePageType.All,
      GooglePageType.Video,
      GooglePageType.Web,
      GooglePageType.News,
      GooglePageType.Books,
    ]

    this.searchStyle.setBaseStyle(this.selectors, { supportPages, forceUpdate })
  }

  /**
   * 初始化结果元数据
   */
  initResultMeta(): void {
    const sourceConfig: PageSearchMetaSourceConfig = {
      [GooglePageType.Video]: {
        base: {
          selector: 'div[jsshadow] > div:last-child > div:last-child > span:first-child',
          container: 'div[jsshadow] > div:last-child > div:last-child',
        },
      },
    }

    const timeConfig: PageSearchMetaTimeConfig = {
      [GooglePageType.Video]: {
        selector: 'div[jsshadow] > div:last-child > div:last-child > span span',
      },
      [GooglePageType.News]: {
        selector: 'a span[data-ts]',
      },
    }

    const selector = this.pageType === GooglePageType.Video
      ? GOOGLE_VIDEO_PAGE_SEARCH_RESULT_META_SELECTOR
      : this.selectors.selector

    this.searchStyle.createResultMeta(
      selector,
      {
        leftConfig: sourceConfig,
        rightConfig: timeConfig,
      },
    )
  }

  /**
   * 更新标题
   */
  updateTitle(): void {
    const supportPages = [
      GooglePageType.All,
      GooglePageType.News,
      GooglePageType.Video,
      GooglePageType.Web,
    ]

    if (!supportPages.includes(this.pageType as GooglePageType))
      return

    const config: SearchPageTitleCleanupConfig = {
      [GooglePageType.News]: {
        titleSelector: 'a div[role=heading]',
      },
    }

    const titleSelector = config[this.pageType]?.titleSelector || 'h3'

    this.searchStyle.updateTitle({
      config,
      container: this.selectors.selector,
      titleSelector,
      syncHighlight: {
        enabled: true,
        contentSelector: 'em, b',
      },
      highlightKeywords: ToolKit.browser.url.getUrlParams('q'),
    })
  }
}
